library ieee;
use ieee.std_logic_1164.all;
use work.types.all;

-- TODO: ARGSTACK, VARSTACK can use the T_REGISTER operand1 instead of SP, such
--		that SP is no longer required. The opcode MUST be set to SP to accomplish
--		this.
-- TODO: SAVEREG and LOADREG classes are no longer required

entity decoder is
	port (
		clk							: in  std_logic;
		reset						: in  std_logic;
	
		instruction 		: in  std_logic_vector(INSTR_SIZE-1 downto 0);

		size						: out std_logic_vector(2 downto 0);
		signed					: out std_logic;
		
		p_descriptor		: out std_logic_vector(1 downto 0);
		o1_descriptor		: out std_logic_vector(1 downto 0);
		o2_descriptor		: out std_logic;
		g_descriptor		: out std_logic_vector(1 downto 0);
		
		is_call					: out std_logic;
		is_ret					: out std_logic;
		is_argstack			: out std_logic;
		is_varstack			: out std_logic;
		is_compare			: out std_logic;
		is_fetch 				: out std_logic;
		--is_regstore			: out std_logic;
		is_conversion		: out std_logic;
		is_trap					: out std_logic;
		is_jump					: out std_logic;

		ex_alu_opcode		: out ALU_OPCODE_SELECT_TYPE;
		ex_alu_in_1			: out ALU_IN_1_SELECT_TYPE;
		ex_alu_in_2			: out ALU_IN_2_SELECT_TYPE;
		
		opcode					: out std_logic_vector(3 downto 0)
	);
end entity;

architecture behaviour of decoder is
	signal	ex_alu_opcode_i		: ALU_OPCODE_SELECT_TYPE;
	signal	ex_alu_in_1_i			: ALU_IN_1_SELECT_TYPE;
	signal	ex_alu_in_2_i			: ALU_IN_2_SELECT_TYPE;
	
	signal decoder_rom 				: std_logic_vector(20 downto 0);
begin
	decoder_rom <= 
		-- COMPUTE ADDRESS (ADDRG, ADDRF, ADDRL, CNST)
		"010000101011000000000" when instruction(15 downto 8) = x"01" else
		-- SAVE REGISTER (SAVEAP, SAVEFP, SAVELP, SAVESP)
		--"000000110011000000000" when instruction(15 downto 8) = x"02" else
		-- ARITHMETIC (ADD, SUB, MUL, ...)
		"000110100000000000000" when instruction(15 downto 8) = x"03" else
		-- ONE OPERAND ARITHMETIC (COMP, NEG)
		"000100100000000000000" when instruction(15 downto 8) = x"04" else
		-- FUNCTION CALL (CALL, SYSCALL)
		"001000110001100000000" when instruction(15 downto 8) = x"05" else
		-- JUMP
		"001000000000000000001" when instruction(15 downto 8) = x"06" else
		-- FUNCTION RETURN (RET)
		"000100100000010000000" when instruction(15 downto 8) = x"07" else
		-- FETCH (INDIR)
		"001000100000000001000" when instruction(15 downto 8) = x"08" else
		-- STACKFRAME (ARGSTACK)
		--"010000001111001000000" when instruction(15 downto 8) = x"09" else
		"010000001011001000000" when instruction(15 downto 8) = x"09" else
		-- STACKFRAME (VARSTACK)
		--"010000001111000100000" when instruction(15 downto 8) = x"0A" else
		"010000001011000100000" when instruction(15 downto 8) = x"0A" else
		-- COMPARE (EQ, GE, GT, LT, LE, NE)
		"010110001101000010000" when instruction(15 downto 8) = x"0C" else
		-- ASGN
		"001011000000000000000" when instruction(15 downto 8) = x"0D" else
		-- LOAD REGISTER (LOADAP, LOADFP, LOADLP, LOADSP)
		--"000100010001000000100" when instruction(15 downto 8) = x"0E" else
		-- CV*
		"000100110001000000010" when instruction(15 downto 8) = x"0F" else
		-- others
		(others => '-');

	-- is_trap (or invalid opcode)
	-- this only leaves the 0x0B instruction as undetected invalid
	is_trap <= instruction(15) or instruction(14) or instruction(13) or instruction(12);

	opcode <= instruction(7 downto 4);
	size <= instruction(3 downto 1);
	signed <= instruction(0);

	is_call <= decoder_rom(8);
	is_ret <= decoder_rom(7);
	is_argstack <= decoder_rom(6);
	is_varstack <= decoder_rom(5);
	is_compare <= decoder_rom(4);
	is_fetch <= decoder_rom(3);
	--is_regstore <= decoder_rom(2);
	is_conversion <= decoder_rom(1);
	is_jump <= decoder_rom(0);

	p_descriptor <= decoder_rom(20 downto 19);
	o1_descriptor <= decoder_rom(18 downto 17);
	o2_descriptor <= decoder_rom(16);
	g_descriptor <= decoder_rom(15 downto 14);

	ex_alu_opcode_i <= T_OPCODE when decoder_rom(9) = '0' else T_ADD;
	ex_alu_in_1_i <= 
		T_OPERAND1 when decoder_rom(11 downto 10) = "00" else
		T_REGISTER when decoder_rom(11 downto 10) = "01" else
		T_ZERO; --when decoder_rom(11 downto 10) = "10";-- else
		--T_SP;
	ex_alu_in_2_i <= 
		T_OPERAND2 when decoder_rom(13 downto 12) = "00" else
		T_PARAMETER when decoder_rom(13 downto 12) = "01" else
		T_ZERO when decoder_rom(13 downto 12) = "10" else
		T_ZERO when decoder_rom(13 downto 12) = "11";
	
	-- buffer for alu opcode/operand signals to allow higher clock speeds
	--		(the ex_alu_* signals are not needed directly. the descriptor
	--		signals however, do need to be computed in the same cycle).
	process(clk, reset, ex_alu_opcode_i, ex_alu_in_1_i, ex_alu_in_2_i) begin
		if (clk'event and clk = '1') then
			if (reset = '1') then
				ex_alu_opcode <= T_OPCODE;
				ex_alu_in_1 <= T_OPERAND1;
				ex_alu_in_2 <= T_OPERAND2;
			else
				ex_alu_opcode <= ex_alu_opcode_i;
				ex_alu_in_1 <= ex_alu_in_1_i;
				ex_alu_in_2 <= ex_alu_in_2_i;
			end if;
		end if;
	end process;							
end behaviour;
